package pl.enigmatic.parser.xml;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.w3c.dom.Element;
import org.w3c.dom.Node;

import pl.enigmatic.math.graph.Tree;
import pl.enigmatic.objects.WrapperObject;
import pl.enigmatic.parser.metadata.Metadata;


public class ParsingInfo<T> {

	public static <T> ParsingInfo<T> create(final Parser<T> parser, final StaticParsingInfo staticInfo, final NodeNameInfo nodeInfo, final Node node) {
		return new ParsingInfo<T>(parser, staticInfo, nodeInfo, node);
	}

	private final ParsingInfoTreeHelper tree;

	private final Parser<T> parser;
	private final StaticParsingInfo staticInfo;
	private final Node node;
	private final NodeNameInfo nodeInfo;
	private final Map<Metadata, String> metadata = new HashMap<Metadata, String>();
	private T target = null;

	private ParsingInfo(final Parser<T> parser, final StaticParsingInfo staticInfo, final NodeNameInfo nodeInfo, final Node node) {
		tree = new ParsingInfoTreeHelper(this);
		this.parser = parser;
		this.staticInfo = staticInfo;
		this.node = node;
		this.nodeInfo = nodeInfo;
	}

	public <X> ParsingInfo<X> createChild(final Parser<X> parser, final NodeNameInfo nodeInfo, final Node node) {
		final ParsingInfo<X> res = new ParsingInfo<X>(parser, staticInfo, nodeInfo, node);
		tree.addChild(res.tree);
		return res;
	}

	public ParsingInfo<T> copy(final Node node, final StaticParsingInfo staticInfo) {
		if (this.node == node || node == null) {
			return this;
		} else {
			return new ParsingInfo<T>(parser, staticInfo, nodeInfo, node);
		}
	}

	public <X> ParsingInfo<X> copy(final Parser<X> parser) {
		return ParsingInfo.create(parser, getStaticInfo(), getNodeInfo(), getNode());
	}

	public Node getNode() {
		return node;
	}

	public Element getElement() {
		if (node instanceof Element) {
			return (Element) node;
		} else {
			return null;
		}
	}

	public NodeNameInfo getNodeInfo() {
		return nodeInfo;
	}

	public StaticParsingInfo getStaticInfo() {
		return staticInfo;
	}

	/**
	 * @param name
	 * @return
	 * @see pl.enigmatic.parser.xml.StaticParsingInfo#getNamedObject(java.lang.String)
	 */
	public Object getNamedObject(final String name) {
		return staticInfo.getNamedObject(name);
	}

	public boolean containsNamedObject(final String name) {
		return staticInfo.containsNamedObject(name);
	}

	public T setTarget(final T target) {
		if (this.target == null) {
			this.target = target;
		}
		return this.target;
	}

	public T getTarget() {
		return target;
	}

	public Parser<T> getParser() {
		return parser;
	}

	public boolean isRoot() {
		return tree.isRoot();
	}

	public boolean nonRoot() {
		return tree.nonRoot();
	}

	public ParsingInfo<?> getRoot() {
		return tree.getRoot().getBase();
	}

	public boolean hasParent() {
		return tree.hasParent();
	}

	public boolean isLeaf() {
		return tree.isLeaf();
	}

	public boolean containsMetadata(final Object key) {
		return metadata.containsKey(key);
	}

	public Set<java.util.Map.Entry<Metadata, String>> metadataEntrySet() {
		return metadata.entrySet();
	}

	public String getMetadata(final Object key) {
		return metadata.get(key);
	}

	public boolean isMetadataEmpty() {
		return metadata.isEmpty();
	}

	public Set<Metadata> metadataKeySet() {
		return metadata.keySet();
	}

	public String putMetadata(final Metadata key, final String value) {
		return metadata.put(key, value);
	}

	public void putAllMetadata(final Map<? extends Metadata, ? extends String> m) {
		metadata.putAll(m);
	}

	public Parser<?> getParentParser() {
		if (tree.hasParent()) {
			return tree.getParent().getBase().getParser();
		} else {
			return null;
		}
	}

	public Parser<?> getRootParser() {
		return tree.getRoot().getBase().getParser();
	}

	public ParsingInfo<?> getParent() {
		return WrapperObject.base(tree.getParent());
	}

}


class ParsingInfoTreeHelper extends Tree<ParsingInfo<?>, ParsingInfoTreeHelper> {

	public ParsingInfoTreeHelper(final ParsingInfo<?> base) {
		super(base);
	}

}
